﻿
package extremefx.i18n {
	import extremefx.DateTime;
	import extremefx.i18n.Calendar;	

	/**
	 * @author Marcelo Volmaro
	 * <p>This is the Taiwanese calendar. It differs from the Gregorian calendar only in the years.</p>
	 * <p>The Taiwanese calendar support a single era starting at January 1, 1912</p>
	 */
	public class TaiwanCalendar extends Calendar {
		private static const TAIWAN_ERA:uint = 1;
		private static var _eraHandler:GregorianEraHandler;
		private static const MIN:DateTime = new DateTime (1912, 1, 1, 0, 0, 0);
		private static const MAX:DateTime = new DateTime (9999, 12, 31, 11, 59, 59);
		
		public function TaiwanCalendar() {
			super();
			if(_eraHandler == null){
				_eraHandler = new GregorianEraHandler();
				_eraHandler.appendEra(1,
					CCGregorian._fixedFromDMY(1, 1, 1912));
			}
			
			_abbrEraNames = ["T.C.E."];
			_eraNames = ["Taiwan current era"];
		}
		
		private function _checkDateTime(pTime:DateTime):void {
			_eraHandler.checkDateTime(pTime);
		}
		
		private function _checkEra(pEra:Array):void {
			var era:int = int(pEra[0]);
			if (era == -1) era = TAIWAN_ERA;
			if (!_eraHandler.validEra(era)) throw new ArgumentError("Era value was not valid.");
			pEra[0] = era;
		}
		
		private function _checkYEG(pYear:int, pEra:Array):int {
			_checkEra(pEra);
			return _eraHandler.gregorianYear(pYear, int(pEra[0]));
		}
		
		protected override function _checkYE(pYear:int, pEra:Array):void {
			_checkYEG(pYear, pEra);
		}
		
		private function _checkYMEG(pYear:int, pMonth:int, pEra:Array):int {
			var gregorianYear:int = _checkYEG(pYear, pEra);
			if (pMonth < 1 || pMonth > 12) throw new RangeError("pMonth", "Month must be between one and twelve.");
			return gregorianYear;
		}
		
		private function _checkYMDEG(pYear:int, pMonth:int, pDay:int, pEra:Array):int {
			var gregorianYear:int = _checkYMEG(pYear, pMonth, pEra);
			_argumentInRange("pDay", pDay, 1, getDaysInMonth(pYear, pMonth, int(pEra[0])));
			return gregorianYear;
		}
		
		public override function addMonths(pTime:DateTime, pMonths:int):DateTime {
			var t:DateTime = CCGregorian._addMonths(pTime, pMonths);
			_checkDateTime(t);
			return t;
		}
		
		public override function addYears(pTime:DateTime, pYears:int):DateTime {
			var t:DateTime = CCGregorian._addYears(pTime, pYears);
			_checkDateTime(t);
			return t;
		}
		
		public override function getDayOfMonth(pTime:DateTime):int {
			_checkDateTime(pTime);
			return CCGregorian._getDayOfMonth(pTime);
		}
		
		public override function getDayOfWeek(pTime:DateTime):int {
			_checkDateTime(pTime);
			var rd:int = CCFixed._fromDateTime(pTime);
			return CCFixed._dayOfWeek(rd);
		}
		
		public override function getDayOfYear(pTime:DateTime):int {
			_checkDateTime(pTime);
			return CCGregorian._getDayOfYear(pTime);
		}
		
		public override function getDaysInMonth(pYear:int, pMonth:int, pEra:int = -1):int {
			var gYear:int = _checkYMEG(pYear, pMonth, [pEra]);
			return CCGregorian._getDaysInMonth(gYear, pMonth);
		}
		
		public override function getDaysInYear(pYear:int, pEra:int = -1):int {
			var gYear:int = _checkYEG(pYear, [pEra]);
			return CCGregorian._getDaysInYear(gYear);
		}
		
		public override function getEra(pTime:DateTime):int {
			var rd:int = CCFixed._fromDateTime(pTime);
			var e:Array = [];
			_eraHandler.eraYear(e, rd);
			return int(e[0]);
		}
		
		public override function getLeapMonth(pYear:int, pEra:int = -1):int {
			return 0;
		}
		
		public override function getMonth(pTime:DateTime):int {
			_checkDateTime(pTime);
			return CCGregorian._getMonth(pTime);
		}
		
		public override function getMonthsInYear(pYear:int, pEra:int = -1):int {
			_checkYE(pYear, [pEra]);
			return 12;
		}
		
		public override function getYear(pTime:DateTime):int {
			return _eraHandler.eraYear([], CCFixed._fromDateTime(pTime));
		}
		
		public override function isLeapDay(pYear:int, pMonth:int, pDay:int, pEra:int= -1):Boolean {
			var gYear:int = _checkYMDEG(pYear, pMonth, pDay, [pEra]);
			return CCGregorian._isLeapDay(gYear, pMonth, pDay);
		}
		
		public override function isLeapMonth(pYear:int, pMonth:int, pEra:int= -1):Boolean {
			_checkYMEG(pYear, pMonth, [pEra]);
			return false;
		}
		
		public override function isLeapYear(pYear:int, pEra:int= -1):Boolean {
			var gYear:int = _checkYEG(pYear, [pEra]);
			return CCGregorian._isLeapYear(gYear);
		}
		
		public override function toDateTime(pYear:int, pMonth:int, pDay:int, pHour:int, pMinute:int, pSecond:int, pMSecond:int, pEra:int = -1):DateTime {
			var gYear:int = _checkYMDEG(pYear, pMonth, pDay, [pEra]);
			_checkHMSM(pHour, pMinute, pSecond, pMSecond);
			return CCGregorian._toDateTime(gYear, pMonth, pDay, pHour, pMinute, pSecond, pMSecond);
		}
		
		public override function toFourDigitYear(pYear:int):int {
			if (pYear < 0) throw new RangeError("pYear", "Non-negative number required.");
			var era:Array = [-1];
			_checkYE(pYear, era);
			return pYear;
		}
		
		public override function get minSupportedDateTime():DateTime {
			return MIN;
		}
		
		public override function get maxSupportedDateTime():DateTime {
			return MAX;
		}
		
		public override function get algorithmType():uint {
			return CalendarAlgorithmType.SOLAR;
		}
		
		public override function get eras():Array {
			return _eraHandler.eras.concat();  
		}
		
		public override function set twoDigitYearMax(pYear:int):void{ 
			_argumentInRange ("value", pYear, 100, _getMaxYear());
			_twoDigitYearMax = pYear;
		}
	}
}
